/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is Forte for Java, Community Edition. The Initial * Developer of the Original Code is Sun Microsystems, Inc. Portions * Copyright 1997-2000 Sun Microsystems, Inc. All Rights Reserved. */ package org.netbeans.modules.httpserver; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.ObjectInput; import java.util.ResourceBundle; import java.util.Hashtable; import java.util.HashSet; import java.util.HashMap; import java.util.StringTokenizer; import java.util.Properties; import java.net.URL; import java.net.MalformedURLException; import java.net.InetAddress; import java.net.UnknownHostException; import java.text.MessageFormat; import org.openide.options.SystemOption; import org.openide.util.NbBundle; import org.openide.util.HelpCtx; import org.openide.util.HttpServer; import org.openide.filesystems.FileObject; import org.openide.NotifyDescriptor; import org.openide.TopManager; /** Options for http server * * @author Ales Novak, Petr Jiricka * @version 0.12, May 5, 1999 */ public class HttpServerSettings extends SystemOption implements HttpServer.Impl { private static final int MAX_START_RETRIES = 5; private static int currentRetries = 0; /** Has this been initialized ? * Becomes true if a "running" getter or setter is called */ static boolean inited = false; /** Contains threads which are or will be asking for access for the given IP address. * @associates Thread*/ private static Hashtable /* InetAddress -> Thread */ whoAsking = new Hashtable(); public static final int SERVER_STARTUP_TIMEOUT = 3000; /** constant for local host */ public static final String LOCALHOST = "local"; // NOI18N /** constant for any host */ public static final String ANYHOST = "any"; // NOI18N public static final String PROP_PORT = "port"; // NOI18N public static final String PROP_HOST = "host"; // NOI18N public static final String PROP_REPOSITORY_BASEURL = "repositoryBaseURL"; // NOI18N public static final String PROP_CLASSPATH_BASEURL = "classpathBaseURL"; // NOI18N public static final String PROP_RUNNING = "running"; // NOI18N public static final String PROP_GRANTED_ADDRESSES = "grantedAddresses"; // NOI18N /** bundle to obtain text information from */ private static ResourceBundle bundle = NbBundle.getBundle(HttpServerSettings.class); /** port */ // private static int port = 8082; //8080 private static final int DEFAULT_PORT = 8082; /** allowed connections hosts - local/any */ private static String host = LOCALHOST; /** mapping of repository to URL */ private static String repositoryBaseURL = "/repository"; // NOI18N /** mapping of classpath to URL */ private static String classpathBaseURL = "/classpath"; // NOI18N /** addresses which have been granted access to the web server */ private static String grantedAddresses = ""; // NOI18N /** Reflects whether the server is actually running, not the running property */ static boolean running = false; private static boolean startStopMessages = true; private static Properties mappedServlets = new Properties(); /** http settings */ public static HttpServerSettings OPTIONS = new HttpServerSettings(); /** last used servlet name */ private static int lastUsedName = 0; /** map names of servlets to paths */ private static HashMap nameMap = new HashMap(); /** Used to remember the state of the running property during the deserialization */ private boolean pendingRunning = true; static final long serialVersionUID =7387407495740535307L; public HttpServerSettings() { } /** This is a project option. */ private boolean isGlobal() { return false; } /** human presentable name */ public String displayName() { return bundle.getString("CTL_HTTP_settings"); } /** getter for running status */ public boolean isRunning() { if (isWriteExternal()) { if (inited) return running; else return true; } if (inited) { return running; } else { // default value, which is true -> start it setRunning(true); return running; } } /** Intended to be called by the thread which succeeded to start the server */ void runSuccess() { synchronized (HttpServerSettings.OPTIONS) { currentRetries = 0; running = true; HttpServerSettings.OPTIONS.notifyAll(); } } /** Intended to be called by the thread which failed to start the server */ void runFailure() { running = false; currentRetries ++; if (currentRetries <= MAX_START_RETRIES) { setPort(getPort() + 1); setRunning(true); } else { currentRetries = 0; TopManager.getDefault().notify(new NotifyDescriptor.Message( NbBundle.getBundle(HttpServerSettings.class).getString("MSG_HTTP_SERVER_START_FAIL"), NotifyDescriptor.Message.WARNING_MESSAGE)); } } /** Restarts the server if it is running - must be called in a synchronized block */ private void restartIfNecessary(boolean printMessages) { if (running) { if (!printMessages) setStartStopMessages(false); HttpServerModule.stopHTTPServer(); HttpServerModule.initHTTPServer(); // messages will be enabled by the server thread } } /** Reads from the serialized state */ public void readExternal (ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); setRunning(pendingRunning); } /** Returns a relative directory URL with a leading and a trailing slash */ private String getCanonicalRelativeURL(String url) { String newURL; if (url.length() == 0) newURL = ""; // NOI18N else { if (url.charAt(0) != '/') newURL = "/" + url; // NOI18N else newURL = url; if (newURL.charAt(newURL.length() - 1) == '/') newURL = newURL.substring(0, newURL.length() - 1); } return newURL; } /** Returns a relative directory URL with a leading and a trailing slash */ /* private String getCanonicalRelativeURL(String url) { String newURL; if (url.length() == 0) newURL = "/"; else { if (url.charAt(0) != '/') newURL = "/" + url; else newURL = url; if (newURL.charAt(newURL.length() - 1) != '/') newURL = newURL + "/"; } return newURL; }*/ /** setter for running status */ public void setRunning(boolean running) { if (isReadExternal()) { pendingRunning = running; return; } inited = true; if (this.running == running) return; synchronized (HttpServerSettings.OPTIONS) { if (running) { // running status is set by another thread HttpServerModule.initHTTPServer(); } else { this.running = false; HttpServerModule.stopHTTPServer(); } } firePropertyChange(PROP_RUNNING, new Boolean(!running), new Boolean(running)); } /** getter for repository base */ public String getRepositoryBaseURL() { return repositoryBaseURL; } /** setter for repository base */ public void setRepositoryBaseURL(String repositoryBaseURL) { // canonical form starts and ends with a / String newURL = getCanonicalRelativeURL(repositoryBaseURL); // check if any change is taking place if (this.repositoryBaseURL.equals(newURL)) return; // implement the change synchronized (HttpServerSettings.OPTIONS) { this.repositoryBaseURL = newURL; restartIfNecessary(false); } firePropertyChange(PROP_REPOSITORY_BASEURL, null, this.repositoryBaseURL); } /** getter for classpath base */ public String getClasspathBaseURL() { return classpathBaseURL; } /** setter for classpath base */ public void setClasspathBaseURL(String classpathBaseURL) { // canonical form starts and ends with a / String newURL = getCanonicalRelativeURL(classpathBaseURL); // check if any change is taking place if (this.classpathBaseURL.equals(newURL)) return; // implement the change synchronized (HttpServerSettings.OPTIONS) { this.classpathBaseURL = newURL; restartIfNecessary(false); } firePropertyChange(PROP_CLASSPATH_BASEURL, null, this.classpathBaseURL); } /** Getter for grantedAddresses property */ public String getGrantedAddresses() { return grantedAddresses; } /** Setter for grantedAccesses property */ public void setGrantedAddresses(String grantedAddresses) { this.grantedAddresses = grantedAddresses; firePropertyChange(PROP_GRANTED_ADDRESSES, null, this.grantedAddresses); } /** setter for port */ public void setPort(int p) { Object old = null; synchronized (HttpServerSettings.OPTIONS) { old = putProperty(PROP_PORT, new Integer(p), false); restartIfNecessary(true); } firePropertyChange(PROP_PORT, old, new Integer(p)); } /** getter for port */ public int getPort() { Object prop = getProperty(PROP_PORT); return ((prop == null) ? DEFAULT_PORT : ((Integer)prop).intValue()); } /** setter for host */ public void setHost(String h) { if (h.equals(ANYHOST) || h.equals(LOCALHOST)) host = h; firePropertyChange(PROP_HOST, null, this.host); } /** getter for host */ public String getHost() { return host; } public void setStartStopMessages(boolean ssm) { startStopMessages = ssm; } public boolean isStartStopMessages() { return startStopMessages; } public HelpCtx getHelpCtx () { return new HelpCtx (HttpServerSettings.class); } /* Access the firePropertyChange from HTTPServer (which holds the enabled prop). */ void firePropertyChange0 (String name, Object oldVal, Object newVal) { firePropertyChange (name, oldVal, newVal); } /** Returns string for localhost */ private String getLocalHost() { try { return InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { return "localhost"; // NOI18N } } /* Implementation of HttpServer.Impl interface */ /** Maps a file object to a URL. Should ensure that the file object is accessible on the given URL. */ public URL getRepositoryURL(FileObject fo) throws MalformedURLException, UnknownHostException { setRunning(true); return new URL("http", getLocalHost(), getPort(), // NOI18N getRepositoryBaseURL() + "/" + fo.getPackageNameExt('/','.')); // NOI18N } /** Maps the repository root to a URL. This URL should serve a page from which repository objects are accessible. */ public URL getRepositoryRoot() throws MalformedURLException, UnknownHostException { setRunning(true); return new URL("http", getLocalHost(), getPort(), getRepositoryBaseURL() + "/"); // NOI18N } /** Maps a resource path to a URL. Should ensure that the resource is accessible on the given URL. * @param resourcePath path of the resource in the classloader format * @see ClassLoader#getResource(java.lang.String) * @see TopManager#systemClassLoader() */ public URL getResourceURL(String resourcePath) throws MalformedURLException, UnknownHostException { setRunning(true); return new URL("http", getLocalHost(), getPort(), getClasspathBaseURL() + // NOI18N (resourcePath.startsWith("/") ? resourcePath : ("/" + resourcePath))); // NOI18N } /** Maps a resource root to a URL. Should ensure that all resources under the root are accessible under an URL * consisting of the returned URL and fully qualified resource name. * @param resourcePath path of the resource in the classloader format * @see ClassLoader#getResource(java.lang.String) * @see TopManager#systemClassLoader() */ public URL getResourceRoot() throws MalformedURLException, UnknownHostException { setRunning(true); return new URL("http", getLocalHost(), getPort(), getClasspathBaseURL() + "/"); // NOI18N } /* public void mapServlet(String urlPath, String className) { lastUsedName++; String name = "NONAME" + lastUsedName; nameMap.put(urlPath, name); mapServlet(className, name, urlPath); } public void unmapServlet(String urlPath) { unmapServlet0((String)nameMap.get(urlPath)); nameMap.remove(urlPath); } private void mapServlet(String className, String name, String urlPath) { if (name.indexOf('.') != -1) throw new IllegalArgumentException("Servlet name may not contain a dot"); synchronized (HttpServerSettings.OPTIONS) { mappedServlets.put("SERVLET." + name + ".CLASS", className); mappedServlets.put("SERVLET." + name + ".PATHS", urlPath); mappedServlets.put("SERVLET." + name + ".Loader", NbLoader.class.getName()); restartIfNecessary(false); } } private void unmapServlet0(String name) { if (name.indexOf('.') != -1) throw new IllegalArgumentException("Servlet name may not contain a dot"); synchronized (HttpServerSettings.OPTIONS) { mappedServlets.remove("SERVLET." + name + ".CLASS"); mappedServlets.remove("SERVLET." + name + ".PATHS"); mappedServlets.remove("SERVLET." + name + ".Loader"); restartIfNecessary(false); } }*/ /** Requests access for address addr. If necessary asks the user. Returns true it the access * has been granted. */ public boolean allowAccess(InetAddress addr) { if (accessAllowedNow(addr)) return true; Thread askThread = null; synchronized (whoAsking) { // one more test in the synchronized block if (accessAllowedNow(addr)) return true; askThread = (Thread)whoAsking.get(addr); if (askThread == null) { askThread = Thread.currentThread(); whoAsking.put(addr, askThread); } } // now ask the user synchronized (HttpServerSettings.class) { if (askThread != Thread.currentThread()) { return accessAllowedNow(addr); } try { MessageFormat format = new MessageFormat(NbBundle.getBundle(HttpServerSettings.class).getString("MSG_AddAddress")); String msg = format.format(new Object[] { addr.getHostAddress() }); NotifyDescriptor nd = new NotifyDescriptor.Confirmation(msg, NotifyDescriptor.YES_NO_OPTION); Object ret = TopManager.getDefault().notify(nd); if (NotifyDescriptor.YES_OPTION.equals(ret)) { appendAddressToGranted(addr.getHostAddress()); return true; } else return false; } finally { whoAsking.remove(addr); } } // end synchronized } /** Checks whether access to the server is now allowed. */ private boolean accessAllowedNow(InetAddress addr) { if (getHost().equals(HttpServerSettings.ANYHOST)) return true; HashSet hs = getGrantedAddressesSet(); if (hs.contains(addr.getHostAddress())) return true; return false; } /** Appends the address to the list of addresses which have been granted access. */ private void appendAddressToGranted(String addr) { synchronized (HttpServerSettings.OPTIONS) { String granted = getGrantedAddresses().trim(); if ((granted.length() > 0) && (granted.charAt(granted.length() - 1) != ';') && (granted.charAt(granted.length() - 1) != ',')) granted += ','; granted += addr; setGrantedAddresses(granted); } } /** Returns a list of addresses which have been granted access to the web server, * including the localhost. Addresses are represented as strings. */ HashSet getGrantedAddressesSet() { HashSet addr = new HashSet(); try { addr.add(InetAddress.getByName("localhost").getHostAddress()); // NOI18N addr.add(InetAddress.getLocalHost().getHostAddress()); } catch (UnknownHostException e) {} StringTokenizer st = new StringTokenizer(getGrantedAddresses(), ",;"); // NOI18N while (st.hasMoreTokens()) { String ipa = st.nextToken(); ipa = ipa.trim(); try { addr.add(InetAddress.getByName(ipa).getHostAddress()); } catch (UnknownHostException e) {} } return addr; } Properties getMappedServlets() { return mappedServlets; } } /* * Log * 30 Gandalf 1.29 1/12/00 Petr Jiricka i18n * 29 Gandalf 1.28 1/11/00 Petr Jiricka Fixed 5133 * 28 Gandalf 1.27 1/9/00 Petr Jiricka Cleanup * 27 Gandalf 1.26 1/4/00 Petr Jiricka Added to project options * 26 Gandalf 1.25 1/3/00 Petr Jiricka Bugfix 5133 * 25 Gandalf 1.24 10/23/99 Ian Formanek NO SEMANTIC CHANGE - Sun * Microsystems Copyright in File Comment * 24 Gandalf 1.23 10/9/99 Petr Jiricka Fixed serialization of * running property in the first startup if the server hasn't been * launched. * 23 Gandalf 1.22 10/7/99 Petr Jiricka Fixed multiple startup * and shutdown at deserialization in some cases * 22 Gandalf 1.21 10/6/99 Petr Jiricka Changes caused by module * (de)serialization * 21 Gandalf 1.20 10/6/99 Petr Jiricka Fixed bug causing the * server to start at IDE shutdown (after the first start of the IDE) * 20 Gandalf 1.19 9/30/99 Petr Jiricka Jetty -> JSWDK * 19 Gandalf 1.18 9/13/99 Petr Jiricka Default port moved to * 8082 * 18 Gandalf 1.17 9/8/99 Petr Jiricka Fixed * NullPointerException at startup * 17 Gandalf 1.16 8/17/99 Petr Jiricka Fixed startup of the * server during the first IDE start * 16 Gandalf 1.15 8/9/99 Ian Formanek Generated Serial Version * UID * 15 Gandalf 1.14 8/9/99 Petr Jiricka Fixed bug with multiple * restarts of the server on IDE startup * 14 Gandalf 1.13 7/3/99 Petr Jiricka * 13 Gandalf 1.12 7/3/99 Petr Jiricka * 12 Gandalf 1.11 6/25/99 Petr Jiricka Removed debug prints * 11 Gandalf 1.10 6/24/99 Petr Jiricka Implements recent * changes in org.openide.util.HttpServer - allowAccess(...) * 10 Gandalf 1.9 6/23/99 Petr Jiricka * 9 Gandalf 1.8 6/22/99 Petr Jiricka * 8 Gandalf 1.7 6/9/99 Ian Formanek ---- Package Change To * org.openide ---- * 7 Gandalf 1.6 6/8/99 Petr Jiricka * 6 Gandalf 1.5 5/31/99 Petr Jiricka * 5 Gandalf 1.4 5/28/99 Petr Jiricka * 4 Gandalf 1.3 5/11/99 Petr Jiricka * 3 Gandalf 1.2 5/11/99 Petr Jiricka * 2 Gandalf 1.1 5/10/99 Petr Jiricka * 1 Gandalf 1.0 5/7/99 Petr Jiricka * $ */